﻿
#include "linux/irqreturn.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <asm/delay.h>


struct gpio_desc{
	int gpio;
	int irq;
    char *name;
    int time;
	struct timer_list key_timer;
} ;

static struct gpio_desc gpios[2] = {
    {115, 0, "trig", },
    {116, 0, "echo", },
};

/* 主设备号                                                                 */
static int major = 0;
static struct class *sr04_class;

/* 环形缓冲区 */
#define BUF_LEN 128
static int g_times[BUF_LEN];
static int r, w;
int count = sizeof(gpios)/sizeof(gpios[0]);

#define NEXT_POS(x) ((x+1) % BUF_LEN)
#define CMD_TRIG 100

static int is_time_buf_empty(void)
{
	return (r == w);
}

static int is_time_buf_full(void)
{
	return (r == NEXT_POS(w));
}

static void put_time(int time)
{
	if (!is_time_buf_full())
	{
		g_times[w] = time;
		w = NEXT_POS(w);
	}
}

static int get_time(void)
{
	int time = 0;
	if (!is_time_buf_empty())
	{
		time = g_times[r];
		r = NEXT_POS(r);
	}
	return time;
}


static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

static ssize_t sr04_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	int time;

	if (is_time_buf_empty() && (file->f_flags & O_NONBLOCK))
		return -EAGAIN;
	
	wait_event_interruptible(gpio_wait, !is_time_buf_empty());
	time = get_time();
	err = copy_to_user(buf, &time, 4);
	
	return 4;
}

static long sr04_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    // send trig 
	switch (cmd)
	{
		case CMD_TRIG:
		{
			//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
			gpio_set_value(gpios[0].gpio, 1);
			udelay(20);
			gpio_set_value(gpios[0].gpio, 0);
		}
	}

	return 0;
}

/* 定义自己的file_operations结构体                                              */
static struct file_operations sr04_drv_fops = {
	.owner	 = THIS_MODULE,
	.read    = sr04_drv_read,
	.unlocked_ioctl = sr04_drv_ioctl,
};


static irqreturn_t sr04_isr(int irq, void *dev_id)
{
	struct gpio_desc *gpio_desc = dev_id;
	static u64 rising_time = 0;
	int val;
	int time;

	val = gpio_get_value(gpio_desc->gpio);

	if(val)
	{
        // record start time : rising time
	    rising_time = ktime_get_ns();
	}
    else
	{
		if(rising_time == 0)//miss interrupt
		{
			return IRQ_HANDLED;
		}

        // time : falling time - rising time
		time = ktime_get_ns() - rising_time;
		put_time(time);
		wake_up_interruptible(&gpio_wait);
	}
	rising_time = 0;
    return IRQ_HANDLED;
}


/* 在入口函数 */
static int __init sr04_drv_init(void)
{
    int err;

    // trig pin
    gpio_request(gpios[0].gpio, gpios[0].name);
	gpio_direction_output(gpios[0].gpio, 0);

    // echo pin
	gpios[1].irq  = gpio_to_irq(gpios[1].gpio);
	err = request_irq(gpios[1].irq, sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "sr04", &gpios[1]);


	/* 注册file_operations 	*/
	major = register_chrdev(0, "sr04", &sr04_drv_fops); 

	sr04_class = class_create(THIS_MODULE, "sr04_class");
	if (IS_ERR(sr04_class)) 
	{
		unregister_chrdev(major, "sr04");
		return PTR_ERR(sr04_class);
	}

	device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "sr04"); /* /dev/sr04 */
	
	return err;
}

/* 有入口函数就应该有出口函数：卸载驱动程序时，就会去调用这个出口函数
 */
static void __exit sr04_drv_exit(void)
{

	device_destroy(sr04_class, MKDEV(major, 0));
	class_destroy(sr04_class);
	unregister_chrdev(major, "sr04");
    
	gpio_free(gpios[0].gpio);
    free_irq(gpios[1].irq, &gpios[1]);
	
}

module_init(sr04_drv_init);
module_exit(sr04_drv_exit);

MODULE_LICENSE("GPL");


